//
// Thread.cpp
// Usage: Thread object
// CreateDate: 2009-04-06
// Author: MaJiuyue
//
#include <string.h>
#include "Thread.h"
#include "Process.h"
#include "SCI.h"
#include "knlvar.h"

Thread::Thread(Process * process)
{
#ifndef _NDEBUG_
	if(sizeof(THREAD_BLOCK)!=0x2000)
		panic("THREAD_BLOCK size incorrect: 0x%x\n", sizeof(THREAD_BLOCK));
#endif
	Resource * res = ResourceManager.RequestResource(
		ServiceCallInterface::PageFrameAllocator::ResourceName
	);

	// Request 2 pages(8K) for thread block
	m_pThread = (THREAD_BLOCK *)res->CallSCI(
		ServiceCallInterface::PageFrameAllocator::SCIID_GetPagesAddr,
		1
	);

	if(m_pThread == NULL)
		panic("No enough kernel memory for new thread.");

	m_pThread->process = process;
	memset(&m_pThread->regs, 0, sizeof(regs_t));
	m_pThread->regs.cs = SELECTOR_LDT_USER_C  | SA_RPL3;
	m_pThread->regs.ds = SELECTOR_LDT_USER_RW | SA_RPL3;
	m_pThread->regs.es = SELECTOR_LDT_USER_RW | SA_RPL3;
	m_pThread->regs.fs = SELECTOR_LDT_USER_RW | SA_RPL3;
	m_pThread->regs.ss = SELECTOR_LDT_USER_RW | SA_RPL3;
	m_pThread->regs.gs = SELECTOR_LDT_USER_RW | SA_RPL3;
	m_pThread->regs.eip = 0x100000;
	m_pThread->regs.esp = 0x80000000;
	m_pThread->regs.eflags = 0x1200;
}

Thread::Thread(const Thread& target, Process * process)
{
	Resource * res = ResourceManager.RequestResource(
		ServiceCallInterface::PageFrameAllocator::ResourceName
	);

	// Request 2 pages(8K) for thread block
	m_pThread = (THREAD_BLOCK *)res->CallSCI(
		ServiceCallInterface::PageFrameAllocator::SCIID_GetPagesAddr,
		1
	);

	// Copy THREAD_BLOCK
	memcpy(m_pThread, target.m_pThread, sizeof(THREAD_BLOCK));
	m_pThread->process = process;
}

Thread::~Thread()
{
	Resource * res = ResourceManager.RequestResource(
		ServiceCallInterface::PageFrameAllocator::ResourceName
	);

	/* Free pages used by THREAD-BLOCK */
	res->CallSCI(
		ServiceCallInterface::PageFrameAllocator::SCIID_FreePagesAddr,
		(u32)m_pThread,
		1
	);
}

// !!!!!!!!!! == NEVER RETURN == !!!!!!!!!!
extern "C" int k_reenter;
void Thread::Restart()
{
	// If current process address space is not this thread's, switch it
	if(GetCurrentProcess() != m_pThread->process)
		m_pThread->process->SwitchContext();

	// Set esp0 field in TSS
	tss.esp0 = (u32)(&m_pThread->regs.ss) + 4;

	k_reenter--;

	// Restore register
	__asm__ __volatile__("movl %%eax, %%esp\t\n"
		"pop %%gs\t\npop %%fs\t\npop %%es\t\npop %%ds\t\n"
		"popal\t\n"
		"add $4, %%esp\t\n"
		"iretl"
		::"a"(&m_pThread->regs)
	);
}

u32 Thread::GetRegisterValue(REGS_INDEX index)
{
	return ((u32 *)&m_pThread->regs)[index];
}

void Thread::SetRegisterValue(REGS_INDEX index, u32 value)
{
	((u32 *)&m_pThread->regs)[index] = value;
}

